Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite IntervalBox to contain an SVector of Intervals #152

Merged
merged 17 commits into from
Jun 3, 2018

Conversation

dpsanders
Copy link
Member

@dpsanders dpsanders commented May 17, 2018

Fixes #150.

Rewrites IntervalBox to contain an SVector.
(Previously IntervalBox was a subtype of StaticVector.
Incorporates #151.

cc @lbenet

@dpsanders dpsanders changed the title Rewrite IntervalBox Rewrite IntervalBox to contain an SVector of Intervals May 17, 2018
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.08%) to 92.165% when pulling 0f8cb49 on rewrite_intervalbox into 777ad9d on master.

@coveralls
Copy link

coveralls commented May 17, 2018

Coverage Status

Coverage decreased (-0.03%) to 92.218% when pulling ce6bcec on rewrite_intervalbox into 777ad9d on master.

@codecov-io
Copy link

codecov-io commented May 17, 2018

Codecov Report

Merging #152 into master will decrease coverage by 0.04%.
The diff coverage is 87.5%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #152      +/-   ##
=========================================
- Coverage   92.24%   92.2%   -0.05%     
=========================================
  Files          21      22       +1     
  Lines        1006    1026      +20     
=========================================
+ Hits          928     946      +18     
- Misses         78      80       +2
Impacted Files Coverage Δ
src/IntervalArithmetic.jl 100% <ø> (ø) ⬆️
src/display.jl 92.04% <100%> (ø) ⬆️
src/multidim/setdiff.jl 100% <100%> (ø) ⬆️
src/multidim/arithmetic.jl 83.33% <83.33%> (ø)
src/multidim/intervalbox.jl 91.3% <88.23%> (+4.63%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 777ad9d...ce6bcec. Read the comment docs.

Copy link
Member

@lbenet lbenet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot! In mi opinion, this is ready to be merged. I guess this closes #149, since it is not anymore needed, except perhaps for the tests.

/(a::IntervalBox, b::Real) = IntervalBox( a.v ./ b )

Base.broadcast(f, X::IntervalBox) = IntervalBox(f.(X.v))
Base.broadcast(f, X::IntervalBox, Y::IntervalBox) = IntervalBox( f.(X.v, Y.v) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I truly think that this broadcasting change is very nice, I just noticed that, in order to use a function (say exp) on an IntervalBox, you do require the dot-notation. The following uses the current commit (93bb8e0) of this PR:

julia> A = IntervalBox(1..2, 3..4)
[1, 2] × [3, 4]

julia> exp.(A) # this is implemented here
[2.71828, 7.38906] × [20.0855, 54.5982]

julia> exp(A) # throwing an error here is uncomfortable!
ERROR: MethodError: no method matching exp(::IntervalArithmetic.IntervalBox{2,Float64})
Closest candidates are:
  exp(::Float16) at math.jl:950
  exp(::Complex{Float16}) at math.jl:951
  exp(::BigFloat) at mpfr.jl:517
  ...

This is sort of uncomfortable, because you force the user to write whatever functions using the dot notation instead of the usual functions. This is actually not a problem, a priori, but then it should be explicitly written somewhere. My idea with #151 was to avoid this, i.e., use directly exp(A).

Should we allow exp(A) to simply return exp.(A), or shall we leave it as it is now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Julia has moved to requiring exp.(v) to make explicit that you want to apply exp element-wise, since exp of a vector does not make sense.

I don't understand the context where exp of an IntervalBox makes sense. I don't actually understand your Taylor example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Julia has moved to requiring exp.(v) to make explicit that you want to apply exp element-wise, since exp of a vector does not make sense.

Fair enough.


IntervalBox(x::Interval) = IntervalBox( (x,) ) # single interval treated as tuple with one element

Base.getindex(X::IntervalBox, i) = X.v[i]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This replaces what it was in line 11, before; do we need Base.@propagate_inbounds here, or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. To be honest I'm not sure, but it probably doesn't hurt.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure either... I've never understood why it was needed. Proceed as you prefer.

@dpsanders
Copy link
Member Author

Just added it.

@dpsanders
Copy link
Member Author

The tests from #151 are included here I believe?

@lbenet
Copy link
Member

lbenet commented May 18, 2018

The tests from #151 are included here I believe?

Yes, but using broadcasting, i.e., requiring using the dot notation. See this comment.

@lbenet
Copy link
Member

lbenet commented May 18, 2018

The kind of subtleties seems important in what I'm doing in TaylorModels. There, from time to time I need to evaluate expressions of the form f( x0 + v ), where x0 is an IntervalBox, and v is an array with TaylorNs (actually, the independent variables). Using this branch, I get the following errors:

julia> x0
IntervalBox(Interval(0.0, 0.0), Interval(1.0, 1.0))

julia> v
2-element Array{TaylorSeries.TaylorN{IntervalArithmetic.Interval{Float64}},1}:
  Interval(1.0, 1.0) x + 𝒪(‖x‖³)
  Interval(1.0, 1.0) y + 𝒪(‖x‖³)

julia> x0 + v
ERROR: MethodError: no method matching +(::IntervalArithmetic.IntervalBox{2,Float64}, ::Array{TaylorSeries.TaylorN{IntervalArithmetic.Interval{Float64}},1})
...

julia> x0 .+ v
ERROR: MethodError: no method matching +(::IntervalArithmetic.IntervalBox{2,Float64}, ::TaylorSeries.TaylorN{IntervalArithmetic.Interval{Float64}})
...

Can we add methods that at least one of these works?

@dpsanders
Copy link
Member Author

I think we would want x0 .+ v to work, and even x0 + v. I also came across similar issues.
But when would exp(x0) be needed?

@lbenet
Copy link
Member

lbenet commented May 19, 2018

I'm not sure if we actually need to have f(::IntervalBox), so let's leave that for the time being.

In any case, it would be nice to have, at least, f( x0 .+ v ) working, with x0::IntervalBox and v::AbstractArray. It certainly can be done manually, but I think it is worth having it as a function by itself.

@dpsanders
Copy link
Member Author

I still do not understand broadcasting properly, so for now the easiest thing will be to define + for an IntervalBox and a Taylor.

@Kolaru
Copy link
Collaborator

Kolaru commented May 19, 2018

I think that defining

Base.broadcast(f, X::IntervalBox, v::AbstractVector) = IntervalBox( f.(X, v)... )

does the trick for all number-interval operations, since it is more specific than something like

Base.broadcast(f, A::Any, v::AbstractVector)

which is probably currently called when using this PR with X .+ v, v being a vector.

@lbenet
Copy link
Member

lbenet commented May 19, 2018

Good point. Certainly it is worth checking.

@lbenet
Copy link
Member

lbenet commented May 21, 2018

I just checked the proposal of @Kolaru , and it does the work. Thanks for the suggestion!

Beside that method, it would be needed as well the one with the arguments exchanged, i.e. Base.broadcast(f, v::AbstractVector, X::IntervalBox).

@dpsanders
Copy link
Member Author

dpsanders commented May 31, 2018

Currently things like
mid.(X) and diam.(X) are failing, i.e. things which are not supposed to return an IntervalBox.

(Unfortunately there were no tests for these.)

@dpsanders
Copy link
Member Author

The solution seems to be the following: explicitly say how to broadcast those functions:

Base.broadcast(::typeof(mid), X::IntervalBox) = mid.(X.v)

@dpsanders
Copy link
Member Author

Other functions:
mig, mag, radius

Also variants of mid with an extra argument.

@Kolaru
Copy link
Collaborator

Kolaru commented Jun 1, 2018

Another solution is to define a function which turn vector of Interval into IntervalBox but leaves others unchanged.

choose_type(v::SVector{Interval}) = IntervalBox(v)
choose_type(v) = v

Then broadcasting becomes

Base.broadcast(f, X::IntervalBox) = choose_type(f.(X.v))

and it should work as expected.

@dpsanders
Copy link
Member Author

@Kolaru That sounds like a great solution, thanks!

@dpsanders
Copy link
Member Author

The failure on 32-bit Windows seems to be a Julia bug: JuliaLang/julia#27402

So I think this is ready to merge.

@lbenet
Copy link
Member

lbenet commented Jun 3, 2018

Merging! Thanks a lot!

@lbenet lbenet merged commit 4657b06 into master Jun 3, 2018
@dpsanders dpsanders deleted the rewrite_intervalbox branch September 25, 2018 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants